/*
============================================================================
 Name        : sd_network.cpp
 Author      : Jian
 Version     :
 Copyright   : GPL
 Description : sd_network.cpp - source file
============================================================================
*/

/* INCLUDE FILES */
#include "sd_network.h"
#include "dd_define.h"
#include "dd_log.h"

#include <e32base.h>	
#include <e32std.h>		
#include <ES_SOCK.H>
#include <CommDbConnPref.h>
#include <in_sock.h>


#define KTestIapId 3

extern HBufC16* SymUtf8ToUnicode(const TDesC8& aUtf8) ;

#define KUndefinedIapId  -1

#define BASELOG1(a, b, c)
#define BASELOG2(a, b, c, f)


/*
*   CSymConnectionManager test define
*
*/

class MSymConnectStateObserver 
{
public:

    enum TState{
        ESucceed, 
        EFailed 
    };

    virtual void ChangeState(TInt aState) = 0 ;

};

class CnmsConnectionMonitor ;

class CSymConnectionManager : public CActive, private MSymConnectStateObserver
{
public:
    
    /* 
     * A block call, may take a few time 
     * return: ETrue connect succeed, EFalse connect failed 
    */
    static TBool StartL(TInt aIapId) ;

    static void StopL() ;

    static void DoDestroy() ;
    
    static TBool IsConnected() ;
    
    /* NOTE: Application should check the IsConnected() function, only the iap is connected, the following 3 functions is meaningful */
    
    static TInt IapId() ;

    static RConnection& Connection() ;

    static RSocketServ& SocketServer() ;
    
    /* DNS */
    static TUint32 ResolveAddressL(const TDesC& aServerName, TInt aIapId) ;

private:

	CSymConnectionManager();

	~CSymConnectionManager();
    
    static void _initSingleton() ;

private:

    TBool _doStartL(TInt aIapId) ;
    
    void _doStop() ;

    TUint32 _doResolve(const TDesC& aServerName) ;

private: 
    
	virtual void RunL();
	
	virtual void DoCancel();

private:

    virtual void ChangeState(TInt aState) ;
    
private:
    static CSymConnectionManager* iSingleton ;
    
    TInt        iIapId ;
    TBool       iIsConnected ;
    RConnection iConnection;
    RSocketServ iSocketServ;
    CnmsConnectionMonitor* iMonitor;
};

#define KnmsConnectionLog         _L("connection.log")

CSymConnectionManager* CSymConnectionManager::iSingleton = NULL ;


class CnmsConnectionMonitor : public CActive
{
public:

    ~CnmsConnectionMonitor() {
        Cancel() ;
    } ;

    static CnmsConnectionMonitor* NewL(RConnection& aConnection, MSymConnectStateObserver& aObserver) {
        CnmsConnectionMonitor* self = new(ELeave) CnmsConnectionMonitor(aConnection, aObserver) ;
        CleanupStack::PushL(self) ;
        CActiveScheduler::Add(self) ;
        self->iConnection.ProgressNotification(self->iProgress, self->iStatus);
        self->SetActive();
        CleanupStack::Pop(self) ;
        return self ;
    };

private:

    CnmsConnectionMonitor(RConnection& aConnection, MSymConnectStateObserver& aObserver): 
       CActive(EPriorityStandard), iConnection(aConnection),iObserver(aObserver) {} ;

private:

    virtual void RunL() {

        BASELOG2(KnmsConnectionLog, _L("CnmsConnectionMonitor::RunL status = %d, state = %d"), iStatus.Int(), iProgress().iStage) ;

        switch (iProgress().iStage)
        {
            // Connection unitialised
        case KConnectionUninitialised:
            break;

            // Starting connetion selection
        case KStartingSelection:
            break;

            // Selection finished
        case KFinishedSelection:
            if (iProgress().iError == KErrNone)
            {
                // The user successfully selected an IAP to be used
            }
            else
            {
                // The user pressed e.g. "Cancel" and did not select an IAP
            }
            break;

            // Connection failure
        case KConnectionFailure:
            iObserver.ChangeState(MSymConnectStateObserver::EFailed) ;
            break;

            // Prepearing connection (e.g. dialing)
        case KPsdStartingConfiguration:
        case KPsdFinishedConfiguration:
        case KCsdFinishedDialling:
        case KCsdScanningScript:
        case KCsdGettingLoginInfo:
        case KCsdGotLoginInfo:
            break;

            // Creating connection (e.g. GPRS activation)
        case KCsdStartingConnect:
        case KCsdFinishedConnect:
            break;

            // Starting log in
        case KCsdStartingLogIn:
            break;

            // Finished login
        case KCsdFinishedLogIn:
            break;

            // Connection open
        case KConnectionOpen:
        case KLinkLayerOpen:

            break;

            // Connection blocked or suspended
        case KDataTransferTemporarilyBlocked:
            break;

            // Hangup or GRPS deactivation
        case KConnectionStartingClose:
            break;

            // Connection closed
        case KConnectionClosed:
        case KLinkLayerClosed:
            iObserver.ChangeState(MSymConnectStateObserver::EFailed) ;
            break;

            // Unhandled state
        default:
            break;
        }

        iConnection.ProgressNotification(iProgress, iStatus);
        SetActive();

    } ;

    virtual TInt RunError(TInt aError) {
        BASELOG1(KnmsConnectionLog, _L("CnmsConnectionMonitor::RunError error = %d"), aError) ;
        iObserver.ChangeState(MSymConnectStateObserver::EFailed) ;
        return KErrNone ;
    } ;

    virtual void DoCancel() {
        iConnection.CancelProgressNotification();
    };

private:

    RConnection&              iConnection ;
    MSymConnectStateObserver& iObserver ;
    TNifProgressBuf           iProgress;
};




CSymConnectionManager::CSymConnectionManager() 
: CActive(EPriorityStandard), iIapId(KUndefinedIapId), iIsConnected(EFalse)
{
}


CSymConnectionManager::~CSymConnectionManager()
{
    if (iMonitor)
        delete iMonitor ;
    Cancel(); 
    iConnection.Close() ;
    iSocketServ.Close() ;
}

void CSymConnectionManager::_initSingleton() 
{
    iSingleton = new CSymConnectionManager ;
    CleanupStack::PushL(iSingleton) ;
    User::LeaveIfError(iSingleton->iSocketServ.Connect());
    User::LeaveIfError(iSingleton->iSocketServ.ShareAuto());
    User::LeaveIfError(iSingleton->iConnection.Open(iSingleton->iSocketServ));
    CActiveScheduler::Add(iSingleton) ;
    CleanupStack::Pop(iSingleton) ;
}

TBool CSymConnectionManager::StartL(TInt aIapId) 
{
    TBool ret = EFalse ;

    TRAPD(err, 
    if (!iSingleton) _initSingleton() ;
    ret = iSingleton->_doStartL(aIapId) ;
    ) ;

    return ret ;
}

void CSymConnectionManager::StopL() 
{
    if (!iSingleton) return ;

    iSingleton->_doStop() ;
}

void CSymConnectionManager::DoDestroy() 
{
    if (iSingleton)
        delete iSingleton ;
}

TBool CSymConnectionManager::IsConnected() 
{
    if (!iSingleton) _initSingleton() ;

    return iSingleton->iIsConnected ;
}


TInt CSymConnectionManager::IapId() 
{
    if (!iSingleton) _initSingleton() ;

    return iSingleton->iIapId ;
}

RConnection& CSymConnectionManager::Connection() 
{
    if (!iSingleton) _initSingleton() ;

    return iSingleton->iConnection ;
}

RSocketServ& CSymConnectionManager::SocketServer() 
{
    if (!iSingleton) _initSingleton() ;

    return iSingleton->iSocketServ ;
}

TUint32 CSymConnectionManager::ResolveAddressL(const TDesC& aServerName, TInt aIapId)
{
    if (StartL(aIapId))
        return iSingleton->_doResolve(aServerName) ;

    return 0 ;
}

TBool CSymConnectionManager::_doStartL(TInt aIapId) 
{
    BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doStartL Try to start a id = %d connection"), aIapId) ;

    if (iIsConnected) 
    {
        BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doStartL is connected, current iap id = %d"), iIapId) ;
        if (aIapId == iIapId)
            return ETrue ;

        _doStop() ;
    }

    if (iMonitor) {
        delete iMonitor ;
        iMonitor = NULL ;
    }

    iIapId = aIapId ;

    TCommDbConnPref connPref;
    connPref.SetDialogPreference(ECommDbDialogPrefDoNotPrompt);
    connPref.SetDirection(ECommDbConnectionDirectionOutgoing);
    connPref.SetBearerSet(ECommDbBearerGPRS);
    connPref.SetIapId(iIapId);

    TInt err = iConnection.Start(connPref);    /* A block call */        

    BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doStartL iConnection start ret = %d"), err) ;

    if (err == KErrNone) 
    {
        iMonitor = CnmsConnectionMonitor::NewL(iConnection, *this) ;

        iIapId = aIapId ;
        iIsConnected = ETrue ;
    }

    return (err == KErrNone) ;
}

void CSymConnectionManager::_doStop() 
{
    BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doStop stop the id = %d connection"), iIapId) ;

    if (iIapId == KUndefinedIapId) return ;

    iConnection.Stop() ;
    iIsConnected = EFalse ;
    iIapId = KUndefinedIapId ;
}

TUint32 CSymConnectionManager::_doResolve(const TDesC& aServerName) 
{
    BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doResolve is call try to resolve the server name = %S"), &aServerName) ;

    TInt err = KErrNone ;
    RHostResolver resolver ;
    if ((err = resolver.Open(iSocketServ, KAfInet, KProtocolInetIcmp, iConnection)) != KErrNone)    // KProtocolInetUdp
    {
        BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doResolve open error = %d"), err) ;
        return 0 ;
    }

    TNameEntry nameEntry;
    if ((err = resolver.GetByName(aServerName, nameEntry)) != KErrNone)
    {
        BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::_doResolve GetByName error = %d"), err) ;
        return 0 ;
    }

    TInetAddr addr = nameEntry().iAddr ;
    return addr.Address() ;
}

void CSymConnectionManager::DoCancel()
{
}

void CSymConnectionManager::RunL()
{

}

void CSymConnectionManager::ChangeState(TInt aState) 
{
    BASELOG1(KnmsConnectionLog, _L("CSymConnectionManager::ChangeState is call and aState = %d"), aState) ;

    if (aState == MSymConnectStateObserver::EFailed) 
    {
        _doStop() ;
    }
}

void sym_network_init() 
{
    CSymConnectionManager::StartL(KTestIapId) ;
}

sd_socket* sd_tcp_open(int noDelay, int quickAck) 
{
    RSocket* socket = new RSocket() ;

    if (!CSymConnectionManager::StartL(KTestIapId))
        return NULL ;

    if (socket->Open(CSymConnectionManager::SocketServer(), KAfInet, KSockStream, KProtocolInetTcp, CSymConnectionManager::Connection()) != KErrNone)
        return NULL ;
    
    return socket ;
}

void sd_tcp_close(sd_socket* sock) 
{
    RSocket* socket = (RSocket*)sock ;

    __DD_ASSERT_DEFENSE(socket, return) ;

    delete socket ;
}

int sd_tcp_connect(sd_socket* sock, DD_UINT ip, DD_UINT port) 
{
    RSocket* socket = (RSocket*)sock ;

    __DD_ASSERT_DEFENSE(socket, return -1) ;

    TInetAddr addr;
    addr.SetAddress(ip) ;
    addr.SetPort(port) ;

    TRequestStatus status = KRequestPending; 
    socket->Connect(addr, status) ;
    User::WaitForRequest(status);

    return (status.Int() == KErrNone) ? 0 : -1;
}

int sd_tcp_send(sd_socket* sock, void* data, int len) 
{
    RSocket* socket = (RSocket*)sock ;

    __DD_ASSERT_DEFENSE(socket && data && len > 0, return -1) ;

    TPtrC8 ptr((const TUint8*)data, len) ;

    TRequestStatus status = KRequestPending; 
    socket->Write(ptr, status);
    User::WaitForRequest(status);

    if (status.Int() != KErrNone) {
        dd_log_error("error: %d got in Write socket", status.Int()) ;
        return -1 ;
    }

    return len ; 
}

int sd_tcp_recv(sd_socket* sock, void* data, int len) 
{
    RSocket* socket = (RSocket*)sock ;

    __DD_ASSERT_DEFENSE(socket, return -1) ;
    
    TSockXfrLength recvLen;
    
    TPtr8 ptr((TUint8*)data, len) ;

    TRequestStatus status = KRequestPending; 
    socket->RecvOneOrMore(ptr, 0, status, recvLen) ;
    User::WaitForRequest(status);
    
    if (status.Int() != KErrNone) {
        dd_log_error("error: %d got in RecvOneOrMore", status.Int()) ;
        return -1 ;
    }

    return recvLen() ;
}


int sd_get_host_ip(const char* host, DD_UINT* ip) 
{
    __DD_ASSERT_DEFENSE(host && ip, return -1) ;

    TUint32 aIp = 0 ;
    TPtrC8 ptr((TUint8*)host, strlen(host)) ;

    TRAPD(
        err,
        HBufC* uDomain = SymUtf8ToUnicode(ptr) ;
        aIp = CSymConnectionManager::ResolveAddressL(*uDomain, KTestIapId) ;
        delete uDomain ;
    ) ;
    
    if (err != KErrNone)
        return -1 ;

    *ip = aIp ;
    return 0;
}

/* End of File */
